package de.loopmode.loopcontrols
{
	import flash.display.DisplayObject;
	import flash.events.MouseEvent;
	
	import mx.controls.List;
	import mx.controls.listClasses.IListItemRenderer;
	import mx.controls.listClasses.ListItemRenderer;
	import flash.events.Event;
 	 
 	/**
 	 * @Author: Jovica Aleksic
 	 * @Email jovica.aleksic@loopmode.de
 	 * 
 	 * Extends the List class by two features that allow the user to cancel a selection made in the list. 
	 * The features are enabled by default, and can be disabled using their variable names.
	 * The features only work when a single item is selected; Multiple selecting is not affected.
	 * 
 	 * "allowDeselectOnToggle": 
 	 * 		Deselects an already-selected item when the user selects it again.
 	 *
 	 * "allowDeselectOnEmpty": 
 	 * 		Clears the selection when the user clicks in empty space of the list. 	 	
 	 *   
 	 */
	public class DeselectableList extends List
	{
		public function DeselectableList()
		{
			super();
		}		
		 
		
		/**
	     *  @private
	     *  Storage for the allowDeselectOnToggle property.
	     */
	    private var _allowDeselectOnToggle:Boolean = false;
		
		
		[Bindable("allowDeselectOnToggleChange")]
	    [Inspectable(category="General", enumeration="false,true", defaultValue="true")]
	
	   /**
		 * A flag that indicates whether you can toggle the selected property of an item.
		 * If <code>true</code>, users can de-select a selected item by clicking on it.
		 * Does not affect multiple selections - only works when the selection contains exactly one item.
		 * Does not affect complex selection (Shift-click, Ctrl-click).
		 * 
		 * @default true
		 */ 
	    public function get allowDeselectOnToggle():Boolean
	    {
	        return _allowDeselectOnToggle;
	    }
	
	
	    /**
	     *  @private
	     */
	    public function set allowDeselectOnToggle(value:Boolean):void
	    {
	        _allowDeselectOnToggle = value;
	        dispatchEvent( new Event( "allowDeselectOnToggleChange" ) );
	    }
	    
		
		/**
	     *  @private
	     *  Storage for the allowDeselectOnEmpty property.
	     */
	    private var _allowDeselectOnEmpty:Boolean = false;
		
		
		[Bindable("allowDeselectOnEmptyChange")]
	    [Inspectable(category="General", enumeration="false,true", defaultValue="true")]
	
	   /**
		 * A flag that indicates whether you can clear the selection by clicking into empty list space.
		 * If <code>true</code>, users can click empty space within the list to unselect any number of selected items.
		 * The list must offer enough height for such empty space to be available to the user below the rendered items.
		 * 
		 * @default true
		 */ 
	    public function get allowDeselectOnEmpty():Boolean
	    {
	        return _allowDeselectOnEmpty;
	    }
	    
	
	    /**
	     *  @private
	     */
	    public function set allowDeselectOnEmpty(value:Boolean):void
	    {
	        _allowDeselectOnEmpty = value;
	        dispatchEvent( new Event( "allowDeselectOnEmptyChange" ) );
	    } 
	  
	  
		/**
		 * @private
		 * @inheritDoc 
		 * 
		 * <i>DeselectableList:</i>
		 * Removes the selection and bails out if the item is already the single selected item of the list.
		 * Does not work if Ctrl or Shift keys  are used.
		 */		
		override protected function selectItem(item:IListItemRenderer, shiftKey:Boolean, ctrlKey:Boolean, transition:Boolean=true):Boolean
		{
			var item_renderer:ListItemRenderer = item as ListItemRenderer;
			if ( this._allowDeselectOnToggle ) {  
				if (this.selectedIndices.length == 1) {
					var item_index:int = item_renderer.listData.rowIndex + this.verticalScrollPosition;
					if (item_index == this.selectedIndex) {
		 		 		if ( !shiftKey && !ctrlKey ) {			 
							this.selectedIndex = -1;  
							return true;
						}
					}
				} 
			}
			return super.selectItem(item, shiftKey, ctrlKey, transition);
		}
		 
		 
		/**
		 * @private
		 * Resets the selectedIndex when the user pressed the mouse within empty space of the list. 
		 * Contained scrollbars and rendered items are not considered empty space.
		 * The click event target must be a direct child of a ListItemRenderer, which is the case for the default flex itemRenderer. 
		 * For usage with more complex item renderers, the code needs to be adjusted.
		 */ 
		override protected function mouseDownHandler(event:MouseEvent):void
		{
			if ( this._allowDeselectOnEmpty ) { 			
	 		 	var click_target:DisplayObject = event.target as DisplayObject;
	 		 	if ( click_target) {
		 		 	var within_rendered_item:Boolean = click_target.parent is ListItemRenderer; // will fail with complex item renderers, where the target of the click event is not a direct child of a ListItemRenderer 
		 		 	var within_h_scrollbar:Boolean = this.horizontalScrollBar && this.horizontalScrollBar.contains( click_target );	
					var within_v_scrollbar:Boolean = this.verticalScrollBar && this.verticalScrollBar.contains( click_target );
		 		 	
		 		 	// if the click target is not child of a rendered item, and also not child of a scrollbar, 
		 		 	// we consider it empty space and clear the selection.
		 		 	if (!within_rendered_item && !within_h_scrollbar && !within_v_scrollbar) {
						this.selectedIndex = -1;
						this.validateNow();
					}
	 		 	}
	 		}
			super.mouseDownHandler(event);
		}
	}
}